﻿/*
 *	Copyright (c) 2009 Queensland University of Technology. All rights reserved.
 *	The QUT Bioinformatics Collection is open source software released under the 
 *	Microsoft Public License (Ms-PL): http://www.microsoft.com/opensource/licenses.mspx.
 */
using System;
using System.ComponentModel;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using QUT.Bio.Map2D;

namespace QUT.Bio.Graphs {

	/// <summary>
	/// Node does two things:
	/// <list>
	/// <item>Adapts an object to make its properties indexable by string.</item>
	/// <item>Binds the object to a (presumably) unique identification value.</item>
	/// </list>
	/// <para>NodeView then uses the string->object indexer to access and (possibly) mutate the properties of an underlying object.</para>
	/// </summary>
	/// <typeparam name="NodeType">The type used for data ids.</typeparam>

	public class NodeView<NodeType> 
		where NodeType : IComparable<NodeType>, IEquatable<NodeType> {

		#region Constructor - all properties
		/// <summary>
		/// Initialises a Node.
		/// </summary>
		/// <param name="node">The node represented by this NodeView.</param>
		/// <param name="canvas">A QUT.Bio.Map2D canvas in which the node will be displayed.</param>
		/// <param name="label">The label to display as part of the LabelledVertex that will be used to portray the node.</param>
		/// <param name="glyph">The icon to display for the node.</param>

		public NodeView (
			Node<NodeType> node,
			FrameworkElement glyph,
			string label,
			Map2DCanvas canvas
		) {
			this.Node = node;

			PositionedElement = canvas.AddVertex( glyph, label, 0, 0 );

			Interpolator = new PositionInterpolator( PositionedElement, 0, 0 );
			LabelledVertex v = LabelledVertex;

			v.Glyph.Cursor = Cursors.Hand;
			v.Glyph.MouseLeftButtonDown += NodeClickEventHandler;

			v.TextBlock.Cursor = Cursors.Hand;
			v.TextBlock.MouseLeftButtonDown += LabelClicked;

			v.FontSize = 9;
		}
		#endregion

		#region Property: Node
		
		/// <summary>
		/// Gets the Node associated with this NodeView.
		/// </summary>
		
		public Node<NodeType> Node {
			get;
			private set;
		}
		#endregion

		#region Property: PositionedElement
		/// <summary>
		/// Gets a reference to the PositionedElement that represents the node in a Map2DCanvas.
		/// </summary>

		public PositionedElement PositionedElement {
			get;
			private set;
		}
		#endregion

		#region Property: Interpolator
		/// <summary>
		/// Gets a reference to a PositionInterpolater that can be used to send the encapsulated
		/// element to new locations in animations.
		/// </summary>

		public PositionInterpolator Interpolator {
			get;
			private set;
		}
		#endregion

		#region MouseShiftDown
		/// <summary>
		/// Gets a boolean that records if the Shift key was down on the most recent mouse click
		/// </summary>

		public bool MouseShiftDown {
			get;
			private set;
		}
		#endregion

		#region Property: MouseAltDown
		/// <summary>
		/// Gets a boolean that records if the Alt key was down on the most recent mouse click
		/// </summary>

		public bool MouseAltDown {
			get;
			private set;
		}
		#endregion

		#region Property: MouseCtlDown
		/// <summary>
		/// Gets a boolean that records if the Control key was down on the most recent mouse click
		/// </summary>

		public bool MouseCtlDown {
			get;
			private set;
		}
		#endregion

		#region Property: MouseX
		/// <summary>
		/// Gets the mouse location at the most recent mouse click, 
		/// relative to the relativePositioningElement.
		/// </summary>

		public double MouseX {
			get;
			private set;
		}
		#endregion

		#region Property: MouseY
		/// <summary>
		/// Gets the mouse location at the most recent mouse click, 
		/// relative to the relativePositioningElement.
		/// </summary>

		public double MouseY {
			get;
			private set;
		}
		#endregion

		private long previousClickTime;
		
		private static long DOUBLECLICK_TICKS_DIFF = 5000000;

		#region Event: NodeEvent
		/// <summary>
		/// An event that fires when the user clicks or double clicks the node, 
		/// or clicks the label.
		/// </summary>

		public event Action<NodeView<NodeType>, NodeAction> NodeEvent;
		#endregion

		#region Property: LabelledVertex
		/// <summary>
		/// Gets a reference to the encapsulated Vertex.
		/// </summary>

		public LabelledVertex LabelledVertex {
			get {
				return (LabelledVertex) PositionedElement.Element;
			}
		}
		#endregion

		#region Method: NodeClickEventHandler
		/// <summary>
		/// Receive mouse clicks and convert them into NodeClick or NodeDoubleClick actions.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="args"></param>

		private void NodeClickEventHandler ( object sender, MouseButtonEventArgs args ) {
			args.Handled = true;
			GetMouseDetails( args );

			long presentTime = System.DateTime.Now.Ticks;
			long elapsedTime = presentTime - previousClickTime;

			if ( elapsedTime <= DOUBLECLICK_TICKS_DIFF ) {
				previousClickTime = 0;
				NotifyAction( NodeAction.NodeDoubleClicked );
			}
			else {
				previousClickTime = presentTime;
				NotifyAction( NodeAction.NodeClicked );
			}
		}
		#endregion

		#region Method: LabelClicked
		/// <summary>
		/// Receive mouse actions and convert them into LabelClicked actions.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="args"></param>

		private void LabelClicked ( object sender, MouseButtonEventArgs args ) {
			args.Handled = true;
			GetMouseDetails( args );
			NotifyAction( NodeAction.LabelClicked );
		}
		#endregion

		#region Method: GetMouseDetails
		/// <summary>
		/// Gets the location and modifiers of the mouse.
		/// </summary>
		/// <param name="args"></param>

		private void GetMouseDetails ( MouseButtonEventArgs args ) {
			Point location = args.GetPosition( null );
			MouseX = location.X;
			MouseY = location.Y;
			MouseShiftDown = Keyboard.Modifiers.CompareTo( ModifierKeys.Shift ) == 0;
			MouseAltDown = Keyboard.Modifiers.CompareTo( ModifierKeys.Alt ) == 0;
			MouseCtlDown = Keyboard.Modifiers.CompareTo( ModifierKeys.Control ) == 0;
		}
		#endregion

		#region Method: NotifyAction
		/// <summary>
		/// Notifies any listeners of an event of interest.
		/// </summary>
		/// <param name="actionType"></param>

		protected void NotifyAction ( NodeAction actionType ) {
			if ( NodeEvent != null ) {
				NodeEvent( this, actionType );
			}
		}
		#endregion
	}
}
